﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using ClearingRestartingAutomaton.Common;

namespace ClearingRestartingAutomaton.Base
{
    /// <summary>
    /// The LanguageSampler class is used to sample languages.
    /// </summary>
    [Serializable]
    public class LanguageSampler
    {
        #region Constructors

        /// <summary>
        /// Default constructor.
        /// </summary>
        public LanguageSampler()
        {
            this.Alphabet = new Alphabet();
        }

        #endregion

        #region Public Properties

        /// <summary>
        /// Alphabet.
        /// </summary>
        public Alphabet Alphabet { get; protected set; }

        #endregion

        #region Public Methods

        /// <summary>
        /// Obtain the language sample form the given automaton.
        /// </summary>
        /// <param name="automaton">Automaton.</param>
        /// <param name="maxCount">Maximal number of words.</param>
        /// <param name="maxLength">Maximal length of words.</param>
        /// <returns>The enumeration of words accepted by the given automaton.</returns>
        public IEnumerable<string> GetSamplesFromAutomaton(Automaton automaton, int maxCount, int maxLength)
        {
            WordTree wordTree = automaton.MakeWordTree(
                String.Empty, 
                StringDefaultValues.LeftDelimiter, 
                StringDefaultValues.RightDelimiter,
                InstructionUse.Generation, maxCount, maxLength);
            return wordTree.TreeSet.Keys.Where(
                word => !String.IsNullOrEmpty(word) && 
                this.Alphabet.IsFromThisAlphabet(word));
        }

        /// <summary>
        /// Obtain the language sample form the given regular expression.
        /// </summary>
        /// <param name="regex">Regular expression.</param>
        /// <param name="maxCount">Maximal number of words.</param>
        /// <param name="maxLength">Maximal length of words.</param>
        /// <returns>The enumeration of words matching the regular expression.</returns>
        public IEnumerable<string> GetSamplesFromRegex(Regex regex, int maxCount, int maxLength)
        {
            return this.GetAllWords(maxCount, maxLength).Where(word => regex.IsMatch(word));
        }

        /// <summary>
        /// Obtain the complement of the specified language sample.
        /// </summary>
        /// <param name="languageSample">Language sample to be complemented.</param>
        /// <param name="maxCount">>Maximal number of words.</param>
        /// <param name="maxLength">Maximal length of words.</param>
        /// <returns>The enumeration of all words from the given alphabet not belonging to the specified language sample.</returns>
        public IEnumerable<string> GetComplement(IEnumerable<string> languageSample, int maxCount, int maxLength)
        {
            HashSet<string> languageSampleSet = new HashSet<string>(languageSample);
            return this.GetAllWords(maxCount, maxLength).Where(word => !languageSampleSet.Contains(word));
        }

        /// <summary>
        /// Obtain the language of all words from the given alphabet.
        /// </summary>
        /// <param name="maxCount">Maximal number of words.</param>
        /// <param name="maxLength">Maximal length of words.</param>
        /// <returns>The enumeration of all words from the given alphabet.</returns>
        public IEnumerable<string> GetAllWords(int maxCount, int maxLength)
        {
            char[] alphabetLetters = this.Alphabet.Letters.ToArray();
            int alphabetSize = alphabetLetters.Length;
            int count = 0;
            for (int length = 1; length <= maxLength; ++length)
            {
                long numberOfWords = 1;
                for (int i = 0; i < length; ++i)
                {
                    numberOfWords *= alphabetSize;
                    if (numberOfWords >= Int32.MaxValue)
                    {
                        numberOfWords = Int32.MaxValue;
                        break;
                    }
                }
                char[] wordLetters = new char[length];
                for (long wordIndex = 0; wordIndex < numberOfWords; ++wordIndex)
                {
                    long actualIndex = wordIndex;
                    for (int i = 0; i < length; ++i)
                    {
                        wordLetters[length - i - 1] = alphabetLetters[actualIndex % alphabetSize];
                        actualIndex /= alphabetSize;
                    }
                    if (count < maxCount)
                    {
                        yield return new String(wordLetters);
                        ++count;
                    }
                    else
                    {
                        yield break;
                    }
                }
            }
        }

        #endregion

        /// <summary>
        /// Returns a System.String that represents this instance.
        /// </summary>
        /// <returns>A System.String that represents this instance.</returns>
        public override string ToString()
        {
            return "Language Sampler on the alphabet " + this.Alphabet.ToString();
        }
    }
}
